package tool

//import dao.ModeDao
import dao.ModeDao
import org.joda.time.DateTime
import org.slf4j.LoggerFactory
import play.api.data.FormBinding
import play.api.http.HttpVerbs
import play.api.libs.json.JsValue
import play.api.mvc._
import tool.Tool.{isTestMode, projectName}
import ujson.play.PlayJson
import upickle.default._
//import shared.VarTool
import utils.Utils

import java.io.File
import java.nio.file.Files
import scala.jdk.CollectionConverters._
//import models.Tables._
import implicits.Implicits._
import play.api.libs.Files.TemporaryFile

import java.net.URLEncoder

/**
 * Created by yz on 17/9/2021
 */
object Tool extends  ProjectTool {

  val logger=LoggerFactory.getLogger(this.getClass)

  val projectName = "gep_liver"
  val dbName = s"${projectName}_database"
  val playPath = new File("../").getAbsolutePath
  val linuxPath = playPath + s"/${dbName}"
  val windowsPath = s"E:\\${dbName}"
  val path = {
    if (new File(windowsPath).exists()) windowsPath else linuxPath
  }
  val windowsTestDir = new File("C:\\temp")
  val linuxTestDir = new File(playPath, "workspace")
  val testDir = if (windowsTestDir.exists()) windowsTestDir else linuxTestDir
  val userDir = new File(path, "user")
  val binPath = new File(path, "bin")
  val exampleDir = new File(path, "example")
  val dataDir = new File(path, "data")
  val featurePlotDir = new File(Tool.dataDir, "feature_plot")
  val dimPlotDir = new File(Tool.dataDir, "dim_plot")
  val imagesDir = new File(Tool.dataDir, "images")
  val cellLineDepDir = new File(Tool.dataDir, "cell_line_dep")
  val barDir = new File(dataDir, "bar")
  val searchCellLineDir = new File(dataDir, "search_cellLine")
  val indexDir = new File(dataDir, "indexs")
  val blastFile = new File(Tool.binPath, "ncbi-blast-2.6.0+/bin/blastn")
  val blastBinFile = new File(Tool.binPath, "ncbi-blast-2.6.0+/bin")
  val makeBlastDbFile = new File(Tool.path, "ncbi-blast-2.6.0+/bin/makeblastdb")
  val blast2HtmlFile = new File(Tool.binPath, "blast2html-82b8c9722996/blast2html.py")
  val blast2HtmlBinDir = new File(Tool.binPath, "blast2html-82b8c9722996")
  val jinjaDir = new File(binPath, "jinjia")
  val enrichBinDir = new File(binPath, "enrich")
  val seqsDir = new File(Tool.path, "seqs")
  val enrichDir = new File(dataDir, "enrich")
  val svBin = new File(binPath, "SV_finder_2.2.1/bin")

  val rPath = {
    val rPath = s"C:\\workspaceForIDEA\\gep_liver_app\\server\\rScripts"
    val linuxRPath = linuxPath + "/rScripts"
    if (new File(rPath).exists()) rPath else linuxRPath
  }

  val scriptHtml =
    """
      |<script>
      |	$(function () {
      |			    $("footer:first").remove()
      |        $("#content").css("margin","0")
      |       $(".linkheader>a").each(function () {
      |				   var text=$(this).text()
      |				   $(this).replaceWith("<span style='color: #222222;'>"+text+"</span>")
      |			   })
      |
      |      $("tr").each(function () {
      |         var a=$(this).find("td>a:last")
      |					var text=a.text()
      |					a.replaceWith("<span style='color: #222222;'>"+text+"</span>")
      |				})
      |
      |       $("p.titleinfo>a").each(function () {
      |				   var text=$(this).text()
      |				   $(this).replaceWith("<span style='color: #606060;'>"+text+"</span>")
      |			   })
      |
      |       $(".param:eq(1)").parent().hide()
      |       $(".linkheader").hide()
      |
      |			})
      |</script>
    """.stripMargin

  val phylotreeCss =
    """
      |<style>
      |.tree-selection-brush .extent {
      |    fill-opacity: .05;
      |    stroke: #fff;
      |    shape-rendering: crispEdges;
      |}
      |
      |.tree-scale-bar text {
      |  font: sans-serif;
      |}
      |
      |.tree-scale-bar line,
      |.tree-scale-bar path {
      |  fill: none;
      |  stroke: #000;
      |  shape-rendering: crispEdges;
      |}
      |
      |.node circle, .node ellipse, .node rect {
      |fill: steelblue;
      |stroke: black;
      |stroke-width: 0.5px;
      |}
      |
      |.internal-node circle, .internal-node ellipse, .internal-node rect{
      |fill: #CCC;
      |stroke: black;
      |stroke-width: 0.5px;
      |}
      |
      |.node {
      |font: 10px sans-serif;
      |}
      |
      |.node-selected {
      |fill: #f00 !important;
      |}
      |
      |.node-collapsed circle, .node-collapsed ellipse, .node-collapsed rect{
      |fill: black !important;
      |}
      |
      |.node-tagged {
      |fill: #00f;
      |}
      |
      |.branch {
      |fill: none;
      |stroke: #999;
      |stroke-width: 2px;
      |}
      |
      |.clade {
      |fill: #1f77b4;
      |stroke: #444;
      |stroke-width: 2px;
      |opacity: 0.5;
      |}
      |
      |.branch-selected {
      |stroke: #f00 !important;
      |stroke-width: 3px;
      |}
      |
      |.branch-tagged {
      |stroke: #00f;
      |stroke-dasharray: 10,5;
      |stroke-width: 2px;
      |}
      |
      |.branch-tracer {
      |stroke: #bbb;
      |stroke-dasharray: 3,4;
      |stroke-width: 1px;
      |}
      |
      |
      |.branch-multiple {
      |stroke-dasharray: 5, 5, 1, 5;
      |stroke-width: 3px;
      |}
      |
      |.branch:hover {
      |stroke-width: 10px;
      |}
      |
      |.internal-node circle:hover, .internal-node ellipse:hover, .internal-node rect:hover {
      |fill: black;
      |stroke: #CCC;
      |}
      |
      |.tree-widget {
      |}
      |</style>
    """.stripMargin

  val availCpu = Runtime.getRuntime.availableProcessors() - 1

  def getOutHtmlFile(dir: File) = {
    new File(dir, "out.html")
  }

  def getOutTableFile(dir: File) = {
    new File(dir, "data.xls")
  }

  def getSeqFile(dir: File) = {
    new File(dir, "seq.fa")
  }

  def getFq1File(dir: File) = {
    new File(dir, "1.fq")
  }

  def getViewFile(dir: File,missionName:String) = {
    new File(dir, s"${missionName}_view.txt")
  }

  def getDataDir(dir: File) = {
    new File(dir, "data")
  }

  def getFq2File(dir: File) = {
    new File(dir, "2.fq")
  }

  def getReportFile(dir: File) = {
    new File(dir, "report.txt")
  }

  def getOutputFile(dir: File) = {
    new File(dir, "output.txt")
  }
//
//  def getUserIdPath(implicit request: RequestHeader) = {
//    val userId = getUserId
//    new File(Tool.userDir, userId.toString)
//  }

//  def getUserDataPath(implicit request: RequestHeader) = {
//    val userIdFile = getUserIdPath
//    new File(userIdFile, "data")
//  }

//  def getSampleFileById(sampleId: Int)(implicit request: RequestHeader) = {
//    val userDataFile = getUserDataPath
//    new File(userDataFile, s"${sampleId}.fa")
//  }

//  def getUserId(implicit request: RequestHeader) = {
//    request.session.get(VarTool.idStr).get.toInt
//  }

//  def getUserIdDir(implicit request: RequestHeader) = {
//    val userId = getUserId
//    new File(Tool.userDir, userId.toString)
//  }

  def getConfigFile(workspaceDir: File) = {
    new File(workspaceDir, "config.json")
  }

  def getUserIdDir(userId: Int) = {
    new File(Tool.userDir, userId.toString)
  }

  def getContentDisposition(url: String) = {
    val encodeUrl = Tool.urlEncode(url)
    s"attachment; filename*=utf-8''${encodeUrl}"
  }

  def urlEncode(url: String) = {
    URLEncoder.encode(url, "UTF-8")
  }

  def fileMoveDir(fieldName: String, dir: File)(implicit request: Request[MultipartFormData[TemporaryFile]]) = {
    fileMoveDirOp(fieldName, dir).get
  }

  def fileMoveFile(fieldName: String, destFile: File)(implicit request: Request[MultipartFormData[TemporaryFile]]) = {
    fileMoveFileOp(fieldName, destFile).get
  }

  def fileMoveDirOp(fieldName: String, dir: File)(implicit request: Request[MultipartFormData[TemporaryFile]]) = {
    val tempFile = request.body.file(fieldName).get
    val destFile = new File(dir, tempFile.filename)
    fileMoveFileOp(fieldName, destFile)
  }

  def fileMoveFileOp(fieldName: String, destFile: File)(implicit request: Request[MultipartFormData[TemporaryFile]]) = {
    val tempFile = request.body.file(fieldName).get
    if (tempFile.ref.path.toFile.length() > 0) {
      tempFile.ref.moveTo(destFile, replace = true)
      Some(destFile)
    } else None
  }

  def createTempDirectory(prefix: String)(implicit modeDao: ModeDao) = {
    if (isTestMode) Tool.testDir else Files.createTempDirectory(prefix).toFile
  }

  def isTestMode(implicit modeDao: ModeDao) = {
    val mode = Utils.execFuture(modeDao.select)
    if (mode.test == "t") true else false
  }

  def deleteDirectory(direcotry: File)(implicit modeDao: ModeDao) = {
    if (!isTestMode) Utils.deleteDirectory(direcotry)
  }

  def json2SeqMap(json: ujson.Value) = {
    json.obj.value.map { case (k, info) =>
      (k, info.str)
    }.toList.toSeqMap
  }

  def jsonParse(jsValue: JsValue) = FormUtils.fromJson(jsValue, Form.FromJsonMaxChars).mapValues(Seq(_))

  def multipartFormParse(body: MultipartFormData[_]) = body.asFormUrlEncoded


  def transformRq(implicit request: Request[AnyContent], formBinding: FormBinding) = {

    val unwrap = request.body match {
      case body: play.api.mvc.AnyContent =>
        body.asFormUrlEncoded.orElse(body.asMultipartFormData).orElse(body.asJson).getOrElse(body)
      case body => body
    }
    unwrap match {
      case body: play.api.libs.json.JsValue =>
        PlayJson.transform(body, reader[ujson.Value])
      case x =>
        val data: Map[String, Seq[String]] = unwrap match {
          case body: Map[_, _] => body.asInstanceOf[Map[String, Seq[String]]]
          case body: MultipartFormData[_] => multipartFormParse(body)
          case Right(body: MultipartFormData[_]) => multipartFormParse(body)
          case _ => Map.empty
        }
        val method: Map[_ <: String, Seq[String]] = request.method.toUpperCase match {
          case HttpVerbs.POST | HttpVerbs.PUT | HttpVerbs.PATCH => Map.empty
          case _ => request.queryString
        }
        val map = (data ++ method).foldLeft(Map.empty[String, String]) {
          case (s, (key, values)) =>
            if (key.endsWith("[]")) {
              val k = key.dropRight(2)
              s ++ values.zipWithIndex.map { case (v, i) => s"$k[$i]" -> v }
            } else {
              s + (key -> values.headOption.getOrElse(""))
            }
        }
        logger.info(s"${map}")
        writeJs(map)
    }
  }

}
